This notebook reports on several approaches to selecting tree stems for individual-scale species classification, uniting Kueppers, Worsham et al. forest inventory data with Falco et al. species classification map derived from the 2018 NEON spectroscopy mission. The approaches range from least to most conservative, that is, from preserving all trees to removing all but those in the uppermost canopy. Maps, agreement statistics, and agreement figures are shown for each approach.

Pipeline

  1. Align geolocated tree stem objects to classification raster.
  2. A. Define a buffer of radius \(r\) around each tree stem object, approximating crown area. B. Segment trees using marker-controlled watershed segmentation algorithm to determine crown area.
  3. Filter crown objects according to one of 4 specified procedures.
  4. Plot a map of crown objects and classification data at an example site.
  5. For each crown object, extract intersecting raster values by majority vote.
  6. Compute accuracy statistics.
  7. Generate agreement figures.

Approaches

  1. Height-dependent buffer and no filtering
  2. Height-dependent buffer and filter on 90th percentile height or intersecting crowns < 90th percentile height
  3. Height-dependent buffer and filter on 90th percentile height or non-intersecting crowns
  4. Height-dependent buffer and filter on 90th percentile height
  5. Static 3m buffer and filter on 90th percentile height
  6. Segment tree-crown polygons and no filtering
  7. Segment tree polygons and filter on 90th percentile height

1. Height dependent buffer and no filtering

The least conservative approach. Keep every LiDAR-detected tree. Set a height-dependent buffer around each object and attempt to extract species within the buffer using majority vote.

Map

Confusion matrix

Classification
ABLA PICO PIEN POTR UNKN
Reference ABLA 321 111 701 0 0
PICO 5 158 12 0 0
PIEN 95 35 486 0 0
POTR 2 5 8 0 0
UNKN 0 0 0 0 0
Kappa Accuracy AccuracyLower AccuracyUpper
0.24 0.5 0.48 0.52
Class Sensitivity Specificity Pos.Pred.Value Neg.Pred.Value Precision Recall F1 Prevalence Detection.Rate Detection.Prevalence Balanced.Accuracy
Class: ABLA 0.76 0.46 0.28 0.87 0.28 0.76 0.41 0.22 0.17 0.58 0.61
Class: PICO 0.51 0.99 0.90 0.91 0.90 0.51 0.65 0.16 0.08 0.09 0.75
Class: PIEN 0.40 0.82 0.79 0.46 0.79 0.40 0.53 0.62 0.25 0.32 0.61
Class: POTR NA 0.99 NA NA 0.00 NA NA 0.00 0.00 0.01 NA
Class: UNKN NA 1.00 NA NA NA NA NA 0.00 0.00 0.00 NA

Agreement: kernel density by species and height

Agreement: frequency by species and size bin


2. Height-dependent buffer and filter on 90th percentile height or intersecting crowns < 90th percentile height

Keep any tree whose height is in the 90th percentile or higher. For all other trees, only keep them if (a) they don’t intersect another tree’s crown area or (b) if the tree whose area they intersect is not in the 90th percentile height.

  1. Apply a height-dependent buffer of radius \(r=0.03H + 0.5\), around every tree.
  2. For each tree \(t_i\):
# Create canopy filter
canopy.filter.a <- unlist(lapply(1:nrow(stem.buff), \(i) {
  if(stem.buff[i,]$Zpred < quantile(stem.buff$Zpred, 0.9)) {
    if(length(stem.within[[i]])<=1) { 
      tst <- stem.buff[i,]$Zpred > 0.9 * stem.buff[stem.overlap[[i]],]$Zpred &
        !any(stem.buff[stem.overlap[[i]],]$Zpred >= quantile(stem.buff$Zpred, 0.9))
      tst <- prod(tst)
    } else { 
      tst <- 0 }
  } else {
    tst <- 1
  }
  as.logical(tst)
}
))

# Apply canopy filter to buffered stems
stem.filt.a <- stem.buff[canopy.filter.a,]

Map

Confusion matrix

Classification
ABLA PICO PIEN POTR UNKN
Reference ABLA 93 39 238 0 0
PICO 2 75 5 0 0
PIEN 33 5 251 0 0
POTR 1 1 5 0 0
UNKN 0 0 0 0 0
Kappa Accuracy AccuracyLower AccuracyUpper
0.31 0.56 0.52 0.6
Class Sensitivity Specificity Pos.Pred.Value Neg.Pred.Value Precision Recall F1 Prevalence Detection.Rate Detection.Prevalence Balanced.Accuracy
Class: ABLA 0.72 0.55 0.25 0.90 0.25 0.72 0.37 0.17 0.12 0.49 0.64
Class: PICO 0.62 0.99 0.91 0.93 0.91 0.62 0.74 0.16 0.10 0.11 0.81
Class: PIEN 0.50 0.85 0.87 0.46 0.87 0.50 0.64 0.67 0.34 0.39 0.68
Class: POTR NA 0.99 NA NA 0.00 NA NA 0.00 0.00 0.01 NA
Class: UNKN NA 1.00 NA NA NA NA NA 0.00 0.00 0.00 NA

Agreement: kernel density by species and height

Agreement: frequency by species and size bin


3. Height-dependent buffer and filter on 90th percentile height or non-intersecting crowns

Keep any tree whose height is in the 90th percentile or higher. For all other trees, only keep them if (a) they don’t intersect another tree’s crown area.

  1. Apply a height-dependent buffer of radius \(r=0.042H + 0.675\), around every tree.
  2. For each tree \(t_i\):
# Create canopy filter
canopy.filter.b <- unlist(lapply(1:nrow(stem.buff), \(i) {
  if(stem.buff[i,]$Zpred < quantile(stem.buff$Zpred, .90)) {
    if(length(stem.within[[i]])<=1) { 
      if(length(stem.overlap[[i]])) {
      tst <- 0
      } else {
        tst <- 1
      }
    } else {
      tst <- 0
    }
  } else {
  tst <- 1
  }
  as.logical(tst)
  }
)) 

# Apply canopy filter
stem.filt.b <- stem.buff[canopy.filter.b,]

Map

Confusion matrix

Classification
ABLA PICO PIEN POTR UNKN
Reference ABLA 65 24 183 0 0
PICO 0 6 1 0 0
PIEN 20 2 181 0 0
POTR 0 1 1 0 0
UNKN 0 0 0 0 0
Kappa Accuracy AccuracyLower AccuracyUpper
0.18 0.52 0.48 0.57
Class Sensitivity Specificity Pos.Pred.Value Neg.Pred.Value Precision Recall F1 Prevalence Detection.Rate Detection.Prevalence Balanced.Accuracy
Class: ABLA 0.76 0.48 0.24 0.91 0.24 0.76 0.36 0.18 0.13 0.56 0.62
Class: PICO 0.18 1.00 0.86 0.94 0.86 0.18 0.30 0.07 0.01 0.01 0.59
Class: PIEN 0.49 0.81 0.89 0.34 0.89 0.49 0.64 0.76 0.37 0.42 0.65
Class: POTR NA 1.00 NA NA 0.00 NA NA 0.00 0.00 0.00 NA
Class: UNKN NA 1.00 NA NA NA NA NA 0.00 0.00 0.00 NA

Agreement: kernel density by species and height

Agreement: frequency by species and size bin


4. Height-dependent buffer and filter on 90th percentile height

Use a height-dependent buffer and keep only trees in the 90th percentile of height or higher.

Map

Confusion matrix

Classification
ABLA PICO PIEN POTR UNKN
Reference ABLA 7 6 41 0 0
PICO 1 21 3 0 0
PIEN 7 7 126 0 0
POTR 0 0 0 0 0
UNKN 0 0 0 0 0
Kappa Accuracy AccuracyLower AccuracyUpper
0.37 0.7 0.64 0.76
Class Sensitivity Specificity Pos.Pred.Value Neg.Pred.Value Precision Recall F1 Prevalence Detection.Rate Detection.Prevalence Balanced.Accuracy
Class: ABLA 0.47 0.77 0.13 0.95 0.13 0.47 0.20 0.07 0.03 0.25 0.62
Class: PICO 0.62 0.98 0.84 0.93 0.84 0.62 0.71 0.16 0.10 0.11 0.80
Class: PIEN 0.74 0.71 0.90 0.44 0.90 0.74 0.81 0.78 0.58 0.64 0.73
Class: POTR NA 1.00 NA NA NA NA NA 0.00 0.00 0.00 NA
Class: UNKN NA 1.00 NA NA NA NA NA 0.00 0.00 0.00 NA

Agreement: kernel density by species and height

Agreement: frequency by species and size bin


5. Static 3m buffer and filter on 90th percentile height

Apply a static 3px buffer around tree objects and keep only trees in the 90th percentile of height or higher.

Map

Confusion matrix

## ABLA PICO PIEN POTR UNKN NA's 
##   47    9  370    0    0    2
Classification
ABLA PICO PIEN POTR UNKN
Reference ABLA 20 0 52 0 0
PICO 0 0 1 0 0
PIEN 4 3 120 0 0
POTR 0 0 0 0 0
UNKN 0 0 0 0 0
Kappa Accuracy AccuracyLower AccuracyUpper
0.26 0.7 0.63 0.76
Class Sensitivity Specificity Pos.Pred.Value Neg.Pred.Value Precision Recall F1 Prevalence Detection.Rate Detection.Prevalence Balanced.Accuracy
Class: ABLA 0.83 0.70 0.28 0.97 0.28 0.83 0.42 0.12 0.1 0.36 0.77
Class: PICO 0.00 0.99 0.00 0.98 0.00 0.00 NaN 0.01 0.0 0.00 0.50
Class: PIEN 0.69 0.74 0.94 0.27 0.94 0.69 0.80 0.86 0.6 0.64 0.72
Class: POTR NA 1.00 NA NA NA NA NA 0.00 0.0 0.00 NA
Class: UNKN NA 1.00 NA NA NA NA NA 0.00 0.0 0.00 NA

Agreement: kernel density by species and height

Agreement: frequency by species and size bin


6. Segment tree-crown polygons and no filtering

Segment crowns and keep all of them.

Maps

CHM with all segmented crowns

Species with all segmented crowns

Confusion matrix

Classification
ABLA PICO PIEN POTR UNKN
Reference ABLA 293 107 533 0 0
PICO 8 150 10 0 0
PIEN 89 39 401 0 0
POTR 1 3 7 0 0
UNKN 0 0 0 0 0
Kappa Accuracy AccuracyLower AccuracyUpper
0.26 0.51 0.49 0.54
Class Sensitivity Specificity Pos.Pred.Value Neg.Pred.Value Precision Recall F1 Prevalence Detection.Rate Detection.Prevalence Balanced.Accuracy
Class: ABLA 0.75 0.49 0.31 0.86 0.31 0.75 0.44 0.24 0.18 0.57 0.62
Class: PICO 0.50 0.99 0.89 0.90 0.89 0.50 0.64 0.18 0.09 0.10 0.74
Class: PIEN 0.42 0.81 0.76 0.51 0.76 0.42 0.54 0.58 0.24 0.32 0.62
Class: POTR NA 0.99 NA NA 0.00 NA NA 0.00 0.00 0.01 NA
Class: UNKN NA 1.00 NA NA NA NA NA 0.00 0.00 0.00 NA

Agreement: kernel density by species and height

Agreement: frequency by species and size bin


7. Segment tree-crown polygons and filter on 90th percentile height

Segment crowns but use only trees in the 90th percentile of height or higher.

Maps

CHM with all segmented crowns

Species with segmented crowns post-height filtering

Confusion matrix

Classification
ABLA PICO PIEN POTR UNKN
Reference ABLA 8 2 27 0 0
PICO 1 20 2 0 0
PIEN 8 9 103 0 0
POTR 0 0 0 0 0
UNKN 0 0 0 0 0
Kappa Accuracy AccuracyLower AccuracyUpper
0.42 0.73 0.66 0.79
Class Sensitivity Specificity Pos.Pred.Value Neg.Pred.Value Precision Recall F1 Prevalence Detection.Rate Detection.Prevalence Balanced.Accuracy
Class: ABLA 0.47 0.82 0.22 0.94 0.22 0.47 0.30 0.09 0.04 0.21 0.65
Class: PICO 0.65 0.98 0.87 0.93 0.87 0.65 0.74 0.17 0.11 0.13 0.81
Class: PIEN 0.78 0.65 0.86 0.52 0.86 0.78 0.82 0.73 0.57 0.67 0.71
Class: POTR NA 1.00 NA NA NA NA NA 0.00 0.00 0.00 NA
Class: UNKN NA 1.00 NA NA NA NA NA 0.00 0.00 0.00 NA

Agreement: kernel density by species and height

Agreement: frequency by species and size bin


Compare accuracy

Run Kappa Accuracy AccuracyLower AccuracyUpper
Ht-Dep Buffer - Least Conserv 0.24 0.50 0.48 0.52
Ht-Dep Buffer - Less Conserv 0.31 0.56 0.52 0.60
Ht-Dep Buffer - More Conserv 0.18 0.52 0.48 0.57
Ht-Dep Buffer - Most Conserv 0.37 0.70 0.64 0.76
Static Buffer - Most Conserv 0.26 0.70 0.63 0.76
Segmented Trees - Least Conserv 0.26 0.51 0.49 0.54
Segmented Trees - Most Conserv 0.42 0.73 0.66 0.79